package br.com.felinelayer.server.application;

import br.com.felinelayer.common.Message;
import br.com.felinelayer.common.Protocol;
import br.com.felinelayer.server.ClientServer;
import java.util.HashMap;

/**
 * Application
 */
public abstract class Application<T extends Application> implements Runnable {

    private int componentId = 0;
    private HashMap<Integer, ComponentServer> components = new HashMap<>();
    private ClientServer client;
//    private FrameServer currentFrame;
    private String applicationName = "";
    protected boolean stopAuto = true;

    protected T showMessage( String message ) {
        boolean wasInBatch = unloadBatch();
        Message c = new Message( Protocol.MSG_NAME_SHOW_MESSAGE );
        c.setValueByName( Protocol.TEXT_SHOW_MESSAGE, message );
        inquire( c );
        if ( wasInBatch ) {
            beginBatch();
        }
        return (T) this;
    }

    protected String showInput( String message ) {
        boolean wasInBatch = unloadBatch();
        Message c = new Message( Protocol.MSG_NAME_SHOW_INPUT );
        c.setValueByName( Protocol.TEXT_SHOW_MESSAGE, message );
        c = inquire( c );
        String response = "";
        if ( c.isPropertyExists( Protocol.SHOW_INPUT_RESPONSE ) ) {
            response = c.getValueByName( Protocol.SHOW_INPUT_RESPONSE );
        }
        if ( wasInBatch ) {
            beginBatch();
        }
        return response;
    }

    protected T sendMessage( Message message ) {
        client.sendMessage( message );
        return (T) this;
    }

    protected Message inquire( Message message ) {
        return client.inquire( message );
    }

    protected boolean inBacth() {
        return client.inBacth();
    }

    protected abstract void doStart();

    protected void doEventReceived( ComponentServer sender, String event ) {
        
    }

    protected void doMessageReceived( Message message ) {
        
    }

    protected void stop() {
        client.disconnect();
    }

    public T setApplicationName( String applicationName ) {
        this.applicationName = applicationName;
        return (T) this;
    }

    public String getApplicationName() {
        return applicationName;
    }

    public T setClient( ClientServer client ) {
        this.client = client;
        return (T) this;
    }

    public T beginBatch() {
        client.beginBatch();
        return (T) this;
    }

    public T endBatch() {
        client.endBatch();
        return (T) this;
    }

    public boolean unloadBatch() {
        boolean wasBatching = inBacth();
        if ( wasBatching ) {
            endBatch();
        }
        return wasBatching;
    }

    public void messageReceived( Message message ) throws Exception {
        beginBatch();
        doMessageReceived( message );
        //System.out.println( message );
        // FelLogger.log( message.toString() );
        // If is event command
        if ( message.getName().equalsIgnoreCase( Protocol.MSG_NAME_EVENT ) ) {
            String event = message.getValueByName( Protocol.EVENT_NAME );
            int id = message.getIntByName( Protocol.EVENT_COMPONENT_ID );
            ComponentServer c = getComponentById( id );
            if ( c == null ) {
                throw new Exception( "Application.messageReceived() Component id "
                        + id + " not found!" );
            }
            // 
            if ( message.isPropertyExists( Protocol.PROPERTY_NAME_TEXT ) ) {
                c.setUpdateFromOtherSide( true );
                c.setText( message.getValueByName( Protocol.PROPERTY_NAME_TEXT ) );
                c.setUpdateFromOtherSide( false );
            }
            // Fire event managment
            doEventReceived( c, event );
            c.messageReceived( message );
        }
        endBatch();
    }

    public ComponentServer getComponentById( int id ) {
        return components.get( new Integer( id ) );
    }

    @SuppressWarnings( "element-type-mismatch" )
    public Application<T> removeComponent( ComponentServer component ) {
        components.remove( component );
        return this;
    }

    private <T extends ComponentServer> T addComponent( FrameServer currentFrame, T component ) {
        component.setApplication( this );
        components.put( new Integer( ++componentId ), component );
        component.setId( componentId );
        Message c = new Message( Protocol.MSG_NAME_CREATE_COMPONENT );
        c.setValueByName( Protocol.COMPONENT_CLASS, component.getComponentClassName() );
        c.setValueByName( Protocol.COMPONENT_ID, componentId );
        // If isn´t frame and don´t have current frame then defines frame id
        if ( !(component instanceof FrameServer) && (currentFrame != null) ) {
            c.setValueByName( Protocol.PARENT_FRAME_ID, currentFrame.getId() );
        }
        // Send message
        client.sendMessage( c );
        //
        component.initialize();
        // 
        return component;
    }

    public FrameServer addFrame() {
        FrameServer f = new FrameServer();
        return addFrame( f );
    }

    public FrameServer addFrame( FrameServer f ) {
        f.setApplication( this );
        return addComponent( null, f );
    }

    public ButtonServer addButton( FrameServer frame ) {
        return addComponent( frame, new ButtonServer() );
    }

    public ComboBoxServer addComboBox( FrameServer frame ) {
        return addComponent( frame, new ComboBoxServer() );
    }

    public EditServer addEdit( FrameServer frame ) {
        return addComponent( frame, new EditServer() );
    }

    public GridServer addGrid( FrameServer frame ) {
        return addComponent( frame, new GridServer() );
    }

    public LabelServer addLabel( FrameServer frame ) {
        return addComponent( frame, new LabelServer() );
    }

    @Override
    public void run() {
        beginBatch();
        doStart();
        endBatch();
        if ( stopAuto ) {
            stop();
        }
    }
}
